package com.segersten.othello.service.akka.game;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

import scala.Option;
import akka.actor.ActorRef;
import akka.actor.UntypedActor;

import com.google.common.collect.Lists;
import com.segersten.othello.model.Board;
import com.segersten.othello.model.Color;
import com.segersten.othello.model.DefaultBoard;
import com.segersten.othello.model.IllegalActionException;
import com.segersten.othello.model.RoundAction;
import com.segersten.othello.service.akka.AkkaUtil;
import com.segersten.othello.service.akka.Auth;
import com.segersten.othello.service.akka.GameObjectPool;
import com.segersten.othello.service.akka.GameObjectReference;
import com.segersten.othello.service.akka.Player;
import com.segersten.othello.service.akka.lobby.BoardReference;
import com.segersten.othello.service.akka.lobby.GameFinishedAction;
import com.segersten.othello.service.akka.lobby.LobbyServiceActor;

public class OthelloBoardServiceActor extends UntypedActor {

	private Board board;
	private Map<Auth, Player> players = new HashMap<Auth, Player>();
	private Integer playerBlackPoints;
	private Integer playerWhitePoints;
	private Player playerBlack;
	private Player playerWhite;
	private String reference;
	private GameObjectPool gameObjectPool;
	
	public OthelloBoardServiceActor(GameObjectPool gameObjectPool, String reference) {
		this.reference = reference;
		this.gameObjectPool = gameObjectPool;
	}

	@Override
	public void preStart() {
		super.preStart();
	}

	@Override
	public void preRestart(Throwable reason, Option<Object> message) {
		super.preRestart(reason, message);
	}

	@Override
	public void postRestart(Throwable reason) {
		super.postRestart(reason);
	}

	@Override
	public void postStop() {
		super.postStop();
	}

	@Override
	public void onReceive(Object message) throws Exception {
		System.out.println("OthelloBoardServiceActor.onReceive : " + message);
		if (message instanceof CreateBoard) {
			if (board != null) {
				throw new IllegalStateException(String.format("Board has already been created for this actor %s",
						message));
			}
			CreateBoard createAction = (CreateBoard) message;
			createBoard(createAction);
			state();
		} else if (message instanceof AuthenticatedAction) {
			AuthenticatedAction moveAction = (AuthenticatedAction) message;
			action(moveAction);
			updateScore();
		} else if (message instanceof State) {
			state();
		} else if (message instanceof AuthenticatedGiveUp) {
			AuthenticatedGiveUp canelAction = (AuthenticatedGiveUp) message;
			cancel(canelAction);
		} else if (message instanceof SessionLogginAction) {
		    SessionLogginAction loginAction = (SessionLogginAction) message;
		    loginAction(loginAction);
		} else {
			throw new IllegalArgumentException("Unknown message: " + message);
		}
	}

	private void loginAction(SessionLogginAction loginAction)
    {
        Auth replacedSession = loginAction.getReplacedSession();
        Player player = loginAction.getPlayer();
        players.remove(replacedSession);
        players.put(player.getAuth(), player);
        if (this.playerWhite.getAuth().equals(replacedSession)) {
            playerWhite=player;
        } else if (this.playerBlack.getAuth().equals(replacedSession)) {
            playerBlack=player;
        } else {
            throw new IllegalStateException("Could not login player " + player + " could not match " + players);
        }
    }

    private void updateScore() {
		if (playerBlackPoints == null && board.getNextColor() == null) {
			playerBlackPoints = board.getColorCount(Color.BLACK);
			playerWhitePoints = board.getColorCount(Color.WHITE);

			notifyLobbyGameFinished();
		}
	}

	private void notifyLobbyGameFinished() {
	    GameObjectReference lobbyActor = gameObjectPool.actorFor(LobbyServiceActor.SERVICE_NAME);
		assertNotNull(playerBlackPoints, "playerBlackPoints");
		assertNotNull(playerWhitePoints, "playerWhitePoints");
		assertNotNull(lobbyActor, "lobbyActor");
		gameObjectPool.tell(lobbyActor, new GameFinishedAction(new BoardReference(getReferenceInteranl()), playerBlack.getAuth(),
				playerBlackPoints, playerWhite.getAuth(), playerWhitePoints));
	}

	private String getReferenceInteranl() {
		return reference;
	}

	private void assertNotNull(Object obj, String msg) {
		if (obj == null) {
			throw new RuntimeException("Was null " + msg);
		}
	}

	private void cancel(AuthenticatedGiveUp giveUpAction) {
		if (players.containsKey(giveUpAction.getAuth()) == false) {
			AkkaUtil.channelTell(this, BoardActionResponse.NonAuth);
			return;
		}

		if (giveUpAction.getAuth().equals(playerBlack.getAuth())) {
			this.playerBlackPoints = -64;
			this.playerWhitePoints = 0;
		} else if (giveUpAction.getAuth().equals(playerWhite.getAuth())) {
			this.playerBlackPoints = 0;
			this.playerWhitePoints = -64;
		} else {
			AkkaUtil.channelTell(this, BoardActionResponse.Unexpexted_error);
			return;
		}

		board.stopGame();
		AkkaUtil.channelTell(this, BoardActionResponse.OK);
		notifyLobbyGameFinished();
	}

	private void state() {
		tellState();
	}

	private void action(AuthenticatedAction authAction) {

		if (board.getNextColor() == null || authAction.getAction().getColor().equals(board.getNextColor()) == false) {
			AkkaUtil.channelTell(this, BoardActionResponse.NotYourTurn);
			return;
		}
		if (players.containsKey(authAction.getAuth()) == false) {
			AkkaUtil.channelTell(this, BoardActionResponse.NonAuth);
			return;
		}
		try {
			Action moveAction = authAction.getAction();
			RoundAction action = moveAction.getPoint() == null ? RoundAction.createPass(moveAction.getColor())
					: RoundAction.createMove(moveAction.getPoint(), moveAction.getColor());
			makeMove(action);
		} catch (IllegalActionException e) {
			AkkaUtil.channelTell(this, BoardActionResponse.NotYourTurn);
			return;
		}
		AkkaUtil.channelTell(this, BoardActionResponse.OK);
	}

	private void createBoard(CreateBoard createAction) {
		List<Player> players = Arrays.asList(createAction.getPlayer1(), createAction.getPlayer2());
		int index = new Random().nextInt(2);
		Player player = players.get(index);
		playerBlack = player;
		this.players.put(player.getAuth(), player);
		player = players.get((index + 1) % 2);
		playerWhite = player;
		this.players.put(player.getAuth(), player);
		board = DefaultBoard.createDefaultBoard(createAction.getSize());
	}

	private void makeMove(RoundAction action) throws IllegalActionException {
		board.putBrick(action);
	}

	private void tellState() {
		// System.out.println(board);
		BoardState2 stateResponse = new BoardState2(playerBlackPoints == null ? 0 : playerBlackPoints,
				playerWhitePoints == null ? 0 : playerWhitePoints, board.getRoundActions().size(),
				board.getNextColor(), Lists.transform(board.getRoundActions(), RoundActionToActionFunction.INSTANCE),
				board.getAvailblePoints(board.getNextColor()));
		AkkaUtil.channelTell(this, stateResponse);
	}

}
